// class
class Promise {
  //构造方法
  constructor(executor) {
    //状态属性
    this.PromiseState = "pending";
    //结果值属性
    this.PromiseResult = undefined;
    //
    this.callbacks = []; // {}
    //resolve 函数
    //更改 promise 实例对象状态为成功, 设置成功的结果值
    let success = (value) => {
      //判断状态是否为 pending
      if (this.PromiseState !== "pending") return;
      //更改 promise 实例对象状态为成功
      this.PromiseState = "fulfilled";
      //设置成功的结果值
      this.PromiseResult = value;
      //状态改变之后, 执行对应的回调
      if (this.callbacks.length > 0) {
        //执行成功的回调
        this.callbacks.forEach((cb) => {
          //执行成功的回调
          cb.ok(this.PromiseResult);
        });
      }
    };

    //reject 函数
    let fail = (reason) => {
      //判断状态是否为 pending
      if (this.PromiseState !== "pending") return;
      //更改 promise 实例对象状态为失败
      this.PromiseState = "rejected";
      //设置成功的结果值
      this.PromiseResult = reason;
      //状态改变之后, 执行对应的回调
      if (this.callbacks.length > 0) {
        //执行失败的回调
        this.callbacks.forEach((cb) => {
          //执行失败的回调
          cb.ng(this.PromiseResult);
        });
      }
    };

    try {
      //执行器函数是一个同步回调
      executor(success, fail);
    } catch (e) {
      //更改 promise 实例对象状态为失败
      fail(e);
    }
  }

  //then方法
  then(onFulfilled, onRejected) {
    //为 onRejected 设置默认值
    if (typeof onRejected !== "function") {
      onRejected = (reason) => {
        throw reason;
      };
    }

    //判断成功的回调
    if (typeof onFulfilled !== "function") {
      onFulfilled = (value) => value;
    }

    //返回一个新的 promise 对象
    return new Promise((resolve, reject) => {
      //callback()
      //声明函数 封装重复代码
      let callback = (type) => {
        try {
          //获取回调函数的返回值
          let res = type(this.PromiseResult);
          //判断
          if (res instanceof Promise) {
            res.then(
              (v) => {
                resolve(v);
              },
              (r) => {
                reject(r);
              }
            );
          } else {
            resolve(res);
          }
        } catch (e) {
          reject(e);
        }
      };
      //判断
      if (this.PromiseState === "fulfilled") {
        setTimeout(() => {
          callback(onFulfilled);
        });
      }

      //失败的情况
      if (this.PromiseState === "rejected") {
        setTimeout(() => {
          callback(onRejected);
        });
      }

      //处理 pending 情况
      if (this.PromiseState === "pending") {
        //保存回调
        this.callbacks.push({
          ok: () => callback(onFulfilled),
          ng: () => callback(onRejected),
        });
      }
    });
  }

  //catch 方法
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  //resolve
  static resolve(value) {
    return new Promise((resolve, reject) => {
      //判断
      if (value instanceof Promise) {
        value.then(
          (v) => {
            resolve(v);
          },
          (r) => {
            reject(r);
          }
        );
      } else {
        //不是 promise
        resolve(value);
      }
    });
  }

  //reject
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }

  //all 方法
  static all(promiseArr) {
    //声明一个数组
    let resultArr = [];
    let counter = 0;
    //返回一个新的 promise    [undefined, 'Yes', 'Yeah']
    return new Promise((resolve, reject) => {
      //遍历promise数组
      promiseArr.forEach((p, index) => {
        //调用 then 方法
        p.then(
          (v) => {
            //当数组中所有的 promise 都为成功的时候, 调用 resolve
            // resultArr.push(v); //使用 push 有顺序问题
            resultArr[index] = v;
            //计数变量加 1
            counter++;
            //判断
            if (counter === promiseArr.length) {
              //表明所有都成功
              resolve(resultArr);
            }
          },
          (r) => {
            reject(r);
          }
        );
      });
    });
  }

  static race(promiseArr) {
    //返回新的 promise 对象
    return new Promise((resolve, reject) => {
      //遍历 promiseArr 数组
      promiseArr.forEach((item) => {
        item.then(
          (v) => {
            resolve(v);
          },
          (r) => {
            reject(r);
          }
        );
      });
    });
  }
}

